+++ /dev/null
-<?xml version="1.0"?>
-<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
- "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd" [
-]>
-<chapter id="gtk-getting-started" xmlns:xi="http://www.w3.org/2003/XInclude">
- <title>Getting Started with GTK+</title>
-
- <para>GTK+ is a <ulink url="http://en.wikipedia.org/wiki/Widget_toolkit">
- widget toolkit</ulink>. Each user interface created by
- GTK+ consists of widgets. This is implemented in C using
- <link linkend="gobject">GObject</link>, an object-oriented framework for C.
- Widgets are organized in a hierachy. The window widget is the main container.
- The user interface is then built by adding buttons, drop-down menus, input
- fields, and other widgets to the window.
- If you are creating complex user interfaces it is recommended to
- use #GtkBuilder and its GTK-specific markup description language, instead of
- assembling the interface manually. You can also use a visual user interface
- editor, like <ulink url="https://glade.gnome.org/">Glade</ulink>.</para>
-
- <para>GTK+ is event-driven. The toolkit listens for events such as
- a click on a button, and passes the event to your application.</para>
-
- <para>This chapter contains some tutorial information to get you
- started with GTK+ programming. It assumes that you have GTK+, its
- dependencies and a C compiler installed and ready to use. If you
- need to build GTK+ itself first, refer to the
- <link linkend="gtk-compiling">Compiling the GTK+ libraries</link>
- section in this reference.</para>
-
- <section>
- <title>Basics</title>
-
- <para>To begin our introduction to GTK, we'll start with a simple
- signal-based Gtk application. This program will create an empty 200 × 200 pixel
- window.</para>
-
- <informalfigure>
- <mediaobject>
- <imageobject>
- <imagedata fileref="window-default.png" format="PNG"/>
- </imageobject>
- </mediaobject>
- </informalfigure>
-
- <informalexample>
- <para>Create a new file with the following content named <filename>example-0.c.</filename></para>
- <programlisting><xi:include href="../../../../examples/window-default.c" parse="text"><xi:fallback>MISSING XINCLUDE CONTENT</xi:fallback></xi:include></programlisting>
- </informalexample>
-
- <para>
- You can compile the program above with GCC using:
- <literallayout>
- <literal>gcc `pkg-config --cflags gtk+-4.0` -o example-0 example-0.c `pkg-config --libs gtk+-4.0`</literal>
- </literallayout>
- </para>
-
- <note><para>For more information on how to compile a GTK+ application, please
- refer to the <link linkend="gtk-compiling">Compiling GTK+ Applications</link>
- section in this reference.</para></note>
-
- <para>All GTK+ applications will, of course, include
- <filename>gtk/gtk.h</filename>, which declares functions, types and
- macros required by GTK+ applications.</para>
-
- <warning><para>Even if GTK+ installs multiple header files, only the
- top-level <filename>gtk/gtk.h</filename> header can be directly included
- by third party code. The compiler will abort with an error if any other
- header is directly included.</para></warning>
-
- <para>In a GTK+ application, the purpose of the main() function is to
- create a #GtkApplication object and run it. In this example a
- #GtkApplication pointer named <varname>app</varname> is called and then
- initialized using gtk_application_new().</para>
-
- <para>When creating a #GtkApplication
- you need to pick an application identifier (a name)
- and input to gtk_application_new() as parameter.
- For this example <varname>org.gtk.example</varname> is used
- but for choosing an identifier for your application see
- <ulink url="https://wiki.gnome.org/HowDoI/ChooseApplicationID">this guide</ulink>.
- Lastly gtk_application_new() takes a GApplicationFlags as input for your
- application, if your application would have special needs.
- </para>
-
- <para>Next the
- <ulink url="https://wiki.gnome.org/HowDoI/GtkApplication">activate signal</ulink>
- is connected to the activate() function above the main() functions.
- The <varname>activate</varname> signal will be sent
- when your application is launched with
- g_application_run() on the line below.
- The gtk_application_run() also takes as arguments the pointers to the command line arguments
- counter and string array; this allows GTK+ to parse specific command line
- arguments that control the behavior of GTK+ itself. The parsed arguments
- will be removed from the array, leaving the unrecognized ones for your
- application to parse.
- </para>
-
- <para>Within g_application_run the activate() signal is sent and
- we then proceed into the <function>activate</function>() function of the
- application. Inside the activate() function we want to construct
- our GTK window, so that a window is shown when the application
- is launched. The call to gtk_application_window_new() will
- create a new #GtkWindow and store it inside the
- <varname>window</varname> pointer. The window will have a frame,
- a title bar, and window controls depending on the platform.</para>
-
- <para>A window title is set using gtk_window_set_title(). This function
- takes a GtkWindow* pointer and a string as input. As our
- <varname>window</varname> pointer is a GtkWidget pointer, we need to cast it
- to GtkWindow*.
- But instead of casting <varname>window</varname> via
- <varname>(GtkWindow*)</varname>,
- <varname>window</varname> can be cast using the macro
- <varname>GTK_WINDOW()</varname>.
- <varname>GTK_WINDOW()</varname> will check if the
- pointer is an instance of the GtkWindow class, before casting, and emit a
- warning if the check fails. More information about this convention
- can be found
- <ulink url="https://developer.gnome.org/gobject/stable/gtype-conventions.html">
- here</ulink>.</para>
-
- <para>Finally the window size is set using gtk_window_set_default_size and
- the window is then shown by GTK via gtk_widget_show().</para>
-
- <para>When you exit the window, by for example pressing the X,
- the g_application_run() in the main loop returns with a number
- which is saved inside an integer named "status". Afterwards, the
- #GtkApplication object is freed from memory with g_object_unref().
- Finally the status integer is returned and the GTK application exits.</para>
-
- <para>While the program is running, GTK+ is receiving
- <firstterm>events</firstterm>. These are typically input events caused by
- the user interacting with your program, but also things like messages from
- the window manager or other applications. GTK+ processes these and as a
- result, <firstterm>signals</firstterm> may be emitted on your widgets.
- Connecting handlers for these signals is how you normally make your
- program do something in response to user input.</para>
-
- <para>The following example is slightly more complex, and tries to
- showcase some of the capabilities of GTK+.</para>
-
- <para>In the long tradition of programming languages and libraries,
- it is called <emphasis>Hello, World</emphasis>.</para>
-
- <informalfigure>
- <mediaobject>
- <imageobject>
- <imagedata fileref="hello-world.png" format="PNG"/>
- </imageobject>
- </mediaobject>
- </informalfigure>
-
- <example id="gtk-getting-started-hello-world">
- <title>Hello World in GTK+</title>
- <para>Create a new file with the following content named example-1.c.</para>
- <programlisting><xi:include href="../../../../examples/hello-world.c" parse="text">
- <xi:fallback>MISSING XINCLUDE CONTENT</xi:fallback></xi:include></programlisting>
- </example>
-
- <para>
- You can compile the program above with GCC using:
- <literallayout>
- <literal>gcc `pkg-config --cflags gtk+-4.0` -o example-1 example-1.c `pkg-config --libs gtk+-4.0`</literal>
- </literallayout>
- </para>
- </section>
-
- <para>As seen above, example-1.c builds further upon example-0.c by adding a
- button to our window, with the label "Hello World". Two new GtkWidget pointers
- are declared to accomplish this, <varname>button</varname> and
- <varname>button_box</varname>. The button_box variable is created to store a
- #GtkButtonBox which is GTK+'s way of controlling the size and layout of buttons.
- The #GtkButtonBox is created and assigned to gtk_button_box_new() which takes a
- #GtkOrientation enum as parameter. The buttons which this box will contain can
- either be stored horizontally or vertically but this does not matter in this
- particular case as we are dealing with only one button. After initializing
- button_box with horizontal orientation, the code adds the button_box widget to the
- window widget using gtk_container_add().</para>
-
- <para>Next the <varname>button</varname> variable is initialized in similar manner.
- gtk_button_new_with_label() is called which returns a GtkButton to be stored inside
- <varname>button</varname>. Afterwards <varname>button</varname> is added to
- our <varname>button_box</varname>.
- Using g_signal_connect the button is connected to a function in our app called
- print_hello(), so that when the button is clicked, GTK will call this function.
- As the print_hello() function does not use any data as input, NULL is passed
- to it. print_hello() calls g_print() with the string "Hello World"
- which will print Hello World in a terminal if the GTK application was started
- from one.</para>
-
- <para>After connecting print_hello(), another signal is connected to the "clicked" state
- of the button using g_signal_connect_swapped(). This functions is similar to
- a g_signal_connect() with the difference lying in how the callback function is
- treated. g_signal_connect_swapped() allow you to specify what the callback
- function should take as parameter by letting you pass it as data. In this case
- the function being called back is gtk_widget_destroy() and the <varname>window</varname>
- pointer is passed to it. This has the effect that when the button is clicked,
- the whole GTK window is destroyed. In contrast if a normal g_signal_connect() were used
- to connect the "clicked" signal with gtk_widget_destroy(), then the <varname>button</varname>
- itself would have been destroyed, not the window.
- More information about creating buttons can be found
- <ulink url="https://wiki.gnome.org/HowDoI/Buttons">here</ulink>.
- </para>
-
- <para>The rest of the code in example-1.c is identical to example-0.c. Next
- section will elaborate further on how to add several GtkWidgets to your GTK
- application.</para>
-
- <section>
- <title>Packing</title>
-
- <para>When creating an application, you'll want to put more than one widget
- inside a window.
- When you want to put more than one widget into a window,
- it becomes important to control how each widget is positioned and sized.
- This is where packing comes in.</para>
-
- <para>GTK+ comes with a large variety of <firstterm>layout containers</firstterm>
- whose purpose it is to control the layout of the child widgets that are
- added to them. See <xref linkend="LayoutContainers"/> for an overview.</para>
-
- <para>The following example shows how the GtkGrid container lets you
- arrange several buttons:</para>
-
- <informalfigure>
- <mediaobject>
- <imageobject>
- <imagedata fileref="grid-packing.png" format="PNG"/>
- </imageobject>
- </mediaobject>
- </informalfigure>
-
- <example id="gtk-getting-started-grid-packing">
- <title>Packing buttons</title>
- <para>Create a new file with the following content named example-2.c.</para>
- <programlisting><xi:include href="../../../../examples/grid-packing.c" parse="text"><xi:fallback>MISSING XINCLUDE CONTENT</xi:fallback></xi:include></programlisting>
- </example>
- <para>
- You can compile the program above with GCC using:
- <literallayout>
- <literal>gcc `pkg-config --cflags gtk+-4.0` -o example-2 example-2.c `pkg-config --libs gtk+-4.0`</literal>
- </literallayout>
- </para>
- </section>
-
- <section>
- <title>Building user interfaces</title>
-
- <para>When construcing a more complicated user interface, with dozens
- or hundreds of widgets, doing all the setup work in C code is
- cumbersome, and making changes becomes next to impossible.</para>
-
- <para>Thankfully, GTK+ supports the separation of user interface
- layout from your business logic, by using UI descriptions in an
- XML format that can be parsed by the #GtkBuilder class.</para>
-
- <example>
- <title>Packing buttons with GtkBuilder</title>
- <para>Create a new file with the following content named example-3.c.</para>
- <programlisting><xi:include href="../../../../examples/builder.c" parse="text"><xi:fallback>MISSING XINCLUDE CONTENT</xi:fallback></xi:include></programlisting>
- <para>Create a new file with the following content named builder.ui.</para>
- <programlisting><xi:include href="../../../../examples/builder.ui" parse="text"><xi:fallback>MISSING XINCLUDE CONTENT</xi:fallback></xi:include></programlisting>
- </example>
- <para>
- You can compile the program above with GCC using:
- <literallayout>
- <literal>gcc `pkg-config --cflags gtk+-4.0` -o example-3 example-3.c `pkg-config --libs gtk+-4.0`</literal>
- </literallayout>
- </para>
-
- <para>Note that GtkBuilder can also be used to construct objects
- that are not widgets, such as tree models, adjustments, etc.
- That is the reason the method we use here is called
- gtk_builder_get_object() and returns a GObject* instead of a
- GtkWidget*.</para>
-
- <para>Normally, you would pass a full path to
- gtk_builder_add_from_file() to make the execution of your program
- independent of the current directory. A common location to install
- UI descriptions and similar data is
- <filename>/usr/share/<replaceable>appname</replaceable></filename>.
- </para>
-
- <para>It is also possible to embed the UI description in the source
- code as a string and use gtk_builder_add_from_string() to load it.
- But keeping the UI description in a separate file has several
- advantages: It is then possible to make minor adjustments to the UI
- without recompiling your program, and, more importantly, graphical
- UI editors such as <ulink url="http://glade.gnome.org">glade</ulink>
- can load the file and allow you to create and modify your UI by
- point-and-click.</para>
- </section>
-
- <section>
- <title>Building applications</title>
-
- <para>An application consists of a number of files:
- <variablelist>
- <varlistentry>
- <term>The binary</term>
- <listitem>This gets installed in <filename>/usr/bin</filename>.</listitem>
- </varlistentry>
- <varlistentry>
- <term>A desktop file</term>
- <listitem>The desktop file provides important information about the application to the desktop shell, such as its name, icon, D-Bus name, commandline to launch it, etc. It is installed in <filename>/usr/share/applications</filename>.</listitem>
- </varlistentry>
- <varlistentry>
- <term>An icon</term>
- <listitem>The icon gets installed in <filename>/usr/share/icons/hicolor/48x48/apps</filename>, where it will be found regardless of the current theme.</listitem>
- </varlistentry>
- <varlistentry>
- <term>A settings schema</term>
- <listitem>If the application uses GSettings, it will install its schema
- in <filename>/usr/share/glib-2.0/schemas</filename>, so that tools
- like dconf-editor can find it.</listitem>
- </varlistentry>
- <varlistentry>
- <term>Other resources</term>
- <listitem>Other files, such as GtkBuilder ui files, are best loaded from
- resources stored in the application binary itself. This eliminates the
- need for most of the files that would traditionally be installed in
- an application-specific location in <filename>/usr/share</filename>.</listitem>
- </varlistentry>
- </variablelist>
- </para>
-
- <para>GTK+ includes application support that is built on top of
- #GApplication. In this tutorial we'll build a simple application by
- starting from scratch, adding more and more pieces over time. Along
- the way, we'll learn about #GtkApplication, templates, resources,
- application menus, settings, #GtkHeaderBar, #GtkStack, #GtkSearchBar,
- #GtkListBox, and more.</para>
-
- <para>The full, buildable sources for these examples can be found
- in the examples/ directory of the GTK+ source distribution, or
- <ulink url="https://gitlab.gnome.org/GNOME/gtk/blob/master/examples">online</ulink> in the GTK+ git repository.
- You can build each example separately by using make with the <filename>Makefile.example</filename>
- file. For more information, see the <filename>README</filename> included in the
- examples directory.</para>
-
- <section>
- <title>A trivial application</title>
-
- <para>When using #GtkApplication, the main() function can be very
- simple. We just call g_application_run() and give it an instance
- of our application class.</para>
-
- <informalexample>
- <programlisting><xi:include href="../../../../examples/application1/main.c" parse="text"><xi:fallback>MISSING XINCLUDE CONTENT</xi:fallback></xi:include></programlisting>
- </informalexample>
-
- <para>All the application logic is in the application class, which
- is a subclass of #GtkApplication. Our example does not yet have any
- interesting functionality. All it does is open a window when it is
- activated without arguments, and open the files it is given, if it
- is started with arguments.</para>
-
- <para>To handle these two cases, we override the activate() vfunc,
- which gets called when the application is launched without commandline
- arguments, and the open() vfunc, which gets called when the application
- is launched with commandline arguments.</para>
-
- <para>To learn more about GApplication entry points, consult the
- GIO <ulink url="https://developer.gnome.org/gio/2.36/GApplication.html#GApplication.description">documentation</ulink>.</para>
-
- <informalexample>
- <programlisting><xi:include href="../../../../examples/application1/exampleapp.c" parse="text"><xi:fallback>MISSING XINCLUDE CONTENT</xi:fallback></xi:include></programlisting>
- </informalexample>
-
- <para>Another important class that is part of the application support
- in GTK+ is #GtkApplicationWindow. It is typically subclassed as well.
- Our subclass does not do anything yet, so we will just get an empty
- window.</para>
-
- <informalexample>
- <programlisting><xi:include href="../../../../examples/application1/exampleappwin.c" parse="text"><xi:fallback>MISSING XINCLUDE CONTENT</xi:fallback></xi:include></programlisting>
- </informalexample>
-
- <para>As part of the initial setup of our application, we also
- create an icon and a desktop file.</para>
-
- <informalfigure>
- <mediaobject>
- <imageobject>
- <imagedata fileref="exampleapp.png" format="PNG"/>
- </imageobject>
- </mediaobject>
- </informalfigure>
-
- <informalexample>
- <programlisting><xi:include href="../../../../examples/application1/org.gtk.exampleapp.desktop" parse="text"><xi:fallback>MISSING XINCLUDE CONTENT</xi:fallback></xi:include></programlisting>
- </informalexample>
-
- <para>Note that <replaceable>@<!-- -->bindir@</replaceable> needs to be replaced
- with the actual path to the binary before this desktop file can be used.</para>
-
- <para>Here is what we've achieved so far:</para>
-
- <informalfigure>
- <mediaobject>
- <imageobject>
- <imagedata fileref="getting-started-app1.png" format="PNG"/>
- </imageobject>
- </mediaobject>
- </informalfigure>
-
- <para>This does not look very impressive yet, but our application
- is already presenting itself on the session bus, it has single-instance
- semantics, and it accepts files as commandline arguments.</para>
- </section>
-
- <section>
- <title>Populating the window</title>
-
- <para>In this step, we use a #GtkBuilder template to associate a
- #GtkBuilder ui file with our application window class.</para>
- <para>Our simple ui file puts a #GtkHeaderBar on top of a #GtkStack
- widget. The header bar contains a #GtkStackSwitcher, which is a
- standalone widget to show a row of 'tabs' for the pages of a #GtkStack.
- </para>
-
- <informalexample>
- <programlisting><xi:include href="../../../../examples/application2/window.ui" parse="text"><xi:fallback>MISSING XINCLUDE CONTENT</xi:fallback></xi:include></programlisting>
- </informalexample>
-
- <para>To make use of this file in our application, we revisit
- our #GtkApplicationWindow subclass, and call
- gtk_widget_class_set_template_from_resource() from the class init
- function to set the ui file as template for this class. We also
- add a call to gtk_widget_init_template() in the instance init
- function to instantiate the template for each instance of our
- class.</para>
-
- <informalexample>
- <programlisting><![CDATA[
- ...
-
-static void
-example_app_window_init (ExampleAppWindow *win)
-{
- gtk_widget_init_template (GTK_WIDGET (win));
-}
-
-static void
-example_app_window_class_init (ExampleAppWindowClass *class)
-{
- gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (class),
- "/org/gtk/exampleapp/window.ui");
-}
-
- ...
- ]]></programlisting>
- <para>(<ulink url="https://gitlab.gnome.org/GNOME/gtk/blob/master/examples/application2/exampleappwin.c">full source</ulink>)</para>
- </informalexample>
-
- <para>You may have noticed that we used the <literal>_from_resource(<!-- -->)</literal> variant
- of the function that sets a template. Now we need to use <ulink url="https://developer.gnome.org/gio/stable/GResource.html">GLib's resource functionality</ulink>
- to include the ui file in the binary. This is commonly done by listing
- all resources in a .gresource.xml file, such as this:
- </para>
-
- <informalexample>
- <programlisting><xi:include href="../../../../examples/application2/exampleapp.gresource.xml" parse="text"><xi:fallback>MISSING XINCLUDE CONTENT</xi:fallback></xi:include></programlisting>
- </informalexample>
-
- <para>This file has to be converted into a C source file that will be
- compiled and linked into the application together with the other source
- files. To do so, we use the glib-compile-resources utility:</para>
-
- <screen>
- glib-compile-resources exampleapp.gresource.xml --target=resources.c --generate-source
- </screen>
-
- <para>Our application now looks like this:</para>
-
- <informalfigure>
- <mediaobject>
- <imageobject>
- <imagedata fileref="getting-started-app2.png" format="PNG"/>
- </imageobject>
- </mediaobject>
- </informalfigure>
- </section>
-
- <section>
- <title>Opening files</title>
-
- <para>In this step, we make our application show the content of
- all the files that it is given on the commandline.</para>
-
- <para>To this end, we add a member to the struct in application
- window subclass and keep a reference to the #GtkStack there.
- The first member of the struct should be the parent type from
- which the class is derived. Here, ExampleAppWindow is derived
- from GtkApplicationWindow.
- The gtk_widget_class_bind_template_child() function
- arranges things so that after instantiating the template, the
- @stack member of the struct will point to the widget of
- the same name from the template.</para>
-
- <informalexample>
- <programlisting><![CDATA[
-...
-
-struct _ExampleAppWindow
-{
- GtkApplicationWindow parent;
-
- GtkWidget *stack;
-};
-
-G_DEFINE_TYPE (ExampleAppWindow, example_app_window, GTK_TYPE_APPLICATION_WINDOW)
-
-...
-
-static void
-example_app_window_class_init (ExampleAppWindowClass *class)
-{
- gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (class),
- "/org/gtk/exampleapp/window.ui");
- gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), ExampleAppWindow, stack);
-}
-
-...
- ]]></programlisting>
- <para>(<ulink url="https://gitlab.gnome.org/GNOME/gtk/blob/master/examples/application3/exampleappwin.c">full source</ulink>)</para>
- </informalexample>
-
- <para>Now we revisit the example_app_window_open() function that
- is called for each commandline argument, and construct a GtkTextView
- that we then add as a page to the stack:</para>
-
- <informalexample>
- <programlisting><![CDATA[
-...
-
-void
-example_app_window_open (ExampleAppWindow *win,
- GFile *file)
-{
- gchar *basename;
- GtkWidget *scrolled, *view;
- gchar *contents;
- gsize length;
-
- basename = g_file_get_basename (file);
-
- scrolled = gtk_scrolled_window_new (NULL, NULL);
- gtk_widget_set_hexpand (scrolled, TRUE);
- gtk_widget_set_vexpand (scrolled, TRUE);
- view = gtk_text_view_new ();
- gtk_text_view_set_editable (GTK_TEXT_VIEW (view), FALSE);
- gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (view), FALSE);
- gtk_container_add (GTK_CONTAINER (scrolled), view);
- gtk_stack_add_titled (GTK_STACK (win->stack), scrolled, basename, basename);
-
- if (g_file_load_contents (file, NULL, &contents, &length, NULL, NULL))
- {
- GtkTextBuffer *buffer;
-
- buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
- gtk_text_buffer_set_text (buffer, contents, length);
- g_free (contents);
- }
-
- g_free (basename);
-}
-
-...
- ]]></programlisting>
- <para>(<ulink url="https://gitlab.gnome.org/GNOME/gtk/blob/master/examples/application3/exampleappwin.c">full source</ulink>)</para>
- </informalexample>
-
- <para>Note that we did not have to touch the stack switcher
- at all. It gets all its information from the stack that it
- belongs to. Here, we are passing the label to show for each
- file as the last argument to the gtk_stack_add_titled()
- function.</para>
-
- <para>Our application is beginning to take shape:</para>
-
- <informalfigure>
- <mediaobject>
- <imageobject>
- <imagedata fileref="getting-started-app3.png" format="PNG"/>
- </imageobject>
- </mediaobject>
- </informalfigure>
- </section>
-
- <section>
- <title>An application menu</title>
-
- <para>An application menu is shown by GNOME shell at the top of the
- screen. It is meant to collect infrequently used actions that affect
- the whole application.</para>
-
- <para>Just like the window template, we specify our application menu
- in a ui file, and add it as a resource to our binary.</para>
-
- <informalexample>
- <programlisting><xi:include href="../../../../examples/application4/app-menu.ui" parse="text"><xi:fallback>MISSING XINCLUDE CONTENT</xi:fallback></xi:include></programlisting>
- </informalexample>
-
- <para>To associate the app menu with the application, we have to call
- gtk_application_set_app_menu(). Since app menus work by activating
- #GActions, we also have to add a suitable set of actions to our
- application.</para>
-
- <para>Both of these tasks are best done in the startup() vfunc,
- which is guaranteed to be called once for each primary application
- instance:</para>
- <informalexample>
- <programlisting>
-...
-
-static void
-preferences_activated (GSimpleAction *action,
- GVariant *parameter,
- gpointer app)
-{
-}
-
-static void
-quit_activated (GSimpleAction *action,
- GVariant *parameter,
- gpointer app)
-{
- g_application_quit (G_APPLICATION (app));
-}
-
-static GActionEntry app_entries[] =
-{
- { "preferences", preferences_activated, NULL, NULL, NULL },
- { "quit", quit_activated, NULL, NULL, NULL }
-};
-
-static void
-example_app_startup (GApplication *app)
-{
- GtkBuilder *builder;
- GMenuModel *app_menu;
- const gchar *quit_accels[2] = { "<Ctrl>Q", NULL };
-
- G_APPLICATION_CLASS (example_app_parent_class)->startup (app);
-
- g_action_map_add_action_entries (G_ACTION_MAP (app),
- app_entries, G_N_ELEMENTS (app_entries),
- app);
- gtk_application_set_accels_for_action (GTK_APPLICATION (app),
- "app.quit",
- quit_accels);
-
- builder = gtk_builder_new_from_resource ("/org/gtk/exampleapp/app-menu.ui");
- app_menu = G_MENU_MODEL (gtk_builder_get_object (builder, "appmenu"));
- gtk_application_set_app_menu (GTK_APPLICATION (app), app_menu);
- g_object_unref (builder);
-}
-
-static void
-example_app_class_init (ExampleAppClass *class)
-{
- G_APPLICATION_CLASS (class)->startup = example_app_startup;
- ...
-}
-
-...
- </programlisting>
- <para>(<ulink url="https://gitlab.gnome.org/GNOME/gtk/blob/master/examples/application4/exampleapp.c">full source</ulink>)</para>
- </informalexample>
-
- <para>Our preferences menu item does not do anything yet,
- but the Quit menu item is fully functional. Note that it
- can also be activated by the usual Ctrl-Q shortcut. The
- shortcut was added with gtk_application_set_accels_for_action().
- </para>
-
- <para>The application menu looks like this:</para>
-
- <informalfigure>
- <mediaobject>
- <imageobject>
- <imagedata fileref="getting-started-app4.png" format="PNG"/>
- </imageobject>
- </mediaobject>
- </informalfigure>
- </section>
-
- <section>
- <title>A preference dialog</title>
-
- <para>A typical application will have a some preferences that
- should be remembered from one run to the next. Even for our
- simple example application, we may want to change the font
- that is used for the content.</para>
-
- <para>We are going to use GSettings to store our preferences.
- GSettings requires a schema that describes our settings:</para>
-
- <informalexample>
- <programlisting><xi:include href="../../../../examples/application5/org.gtk.exampleapp.gschema.xml" parse="text"><xi:fallback>MISSING XINCLUDE CONTENT</xi:fallback></xi:include></programlisting>
- </informalexample>
-
- <para>Before we can make use of this schema in our application,
- we need to compile it into the binary form that GSettings
- expects. GIO provides <ulink url="https://developer.gnome.org/gio/2.36/ch31s06.html">macros</ulink>
- to do this in autotools-based projects.</para>
-
- <para>Next, we need to connect our settings to the widgets
- that they are supposed to control. One convenient way to do
- this is to use GSettings bind functionality to bind settings
- keys to object properties, as we do here for the transition
- setting.</para>
-
- <informalexample>
- <programlisting><![CDATA[
-...
-
-static void
-example_app_window_init (ExampleAppWindow *win)
-{
- gtk_widget_init_template (GTK_WIDGET (win));
- win->settings = g_settings_new ("org.gtk.exampleapp");
-
- g_settings_bind (win->settings, "transition",
- win->stack, "transition-type",
- G_SETTINGS_BIND_DEFAULT);
-}
-
-...
- ]]></programlisting>
- <para>(<ulink url="https://gitlab.gnome.org/GNOME/gtk/blob/master/examples/application5/exampleappwin.c">full source</ulink>)</para>
- </informalexample>
-
- <para>The code to connect the font setting is a little more involved,
- since there is no simple object property that it corresponds to, so
- we are not going to go into that here.</para>
-
- <para>At this point, the application will already react if you
- change one of the settings, e.g. using the gsettings commandline
- tool. Of course, we expect the application to provide a preference
- dialog for these. So lets do that now. Our preference dialog will
- be a subclass of GtkDialog, and we'll use the same techniques that
- we've already seen: templates, private structs, settings
- bindings.</para>
-
- <para>Lets start with the template.</para>
-
- <informalexample>
- <programlisting><xi:include href="../../../../examples/application6/prefs.ui" parse="text"><xi:fallback>MISSING XINCLUDE CONTENT</xi:fallback></xi:include></programlisting>
- </informalexample>
-
- <para>Next comes the dialog subclass.</para>
-
- <informalexample>
- <programlisting><xi:include href="../../../../examples/application6/exampleappprefs.c" parse="text"><xi:fallback>MISSING XINCLUDE CONTENT</xi:fallback></xi:include></programlisting>
- </informalexample>
-
- <para>Now we revisit the <literal>preferences_activated(<!-- -->)</literal> function in our
- application class, and make it open a new preference dialog.</para>
-
- <informalexample>
- <programlisting><![CDATA[
-...
-
-static void
-preferences_activated (GSimpleAction *action,
- GVariant *parameter,
- gpointer app)
-{
- ExampleAppPrefs *prefs;
- GtkWindow *win;
-
- win = gtk_application_get_active_window (GTK_APPLICATION (app));
- prefs = example_app_prefs_new (EXAMPLE_APP_WINDOW (win));
- gtk_window_present (GTK_WINDOW (prefs));
-}
-
-...
- ]]></programlisting>
- <para>(<ulink url="https://gitlab.gnome.org/GNOME/gtk/blob/master/examples/application6/exampleapp.c">full source</ulink>)</para>
- </informalexample>
-
- <para>After all this work, our application can now show
- a preference dialog like this:</para>
-
- <informalfigure>
- <mediaobject>
- <imageobject>
- <imagedata fileref="getting-started-app6.png" format="PNG"/>
- </imageobject>
- </mediaobject>
- </informalfigure>
- </section>
-
- <section>
- <title>Adding a search bar</title>
-
- <para>We continue to flesh out the functionality of our application.
- For now, we add search. GTK+ supports this with #GtkSearchEntry and
- #GtkSearchBar. The search bar is a widget that can slide in from the
- top to present a search entry.</para>
-
- <para>We add a toggle button to the header bar, which can be used
- to slide out the search bar below the header bar.</para>
-
- <informalexample>
- <programlisting><xi:include href="../../../../examples/application7/window.ui" parse="text"><xi:fallback>MISSING XINCLUDE CONTENT</xi:fallback></xi:include></programlisting>
- </informalexample>
-
- <para>Implementing the search needs quite a few code changes that
- we are not going to completely go over here. The central piece of
- the search implementation is a signal handler that listens for
- text changes in the search entry.</para>
-
- <informalexample>
- <programlisting><![CDATA[
-...
-
-static void
-search_text_changed (GtkEntry *entry,
- ExampleAppWindow *win)
-{
- const gchar *text;
- GtkWidget *tab;
- GtkWidget *view;
- GtkTextBuffer *buffer;
- GtkTextIter start, match_start, match_end;
-
- text = gtk_entry_get_text (entry);
-
- if (text[0] == '\0')
- return;
-
- tab = gtk_stack_get_visible_child (GTK_STACK (win->stack));
- view = gtk_bin_get_child (GTK_BIN (tab));
- buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
-
- /* Very simple-minded search implementation */
- gtk_text_buffer_get_start_iter (buffer, &start);
- if (gtk_text_iter_forward_search (&start, text, GTK_TEXT_SEARCH_CASE_INSENSITIVE,
- &match_start, &match_end, NULL))
- {
- gtk_text_buffer_select_range (buffer, &match_start, &match_end);
- gtk_text_view_scroll_to_iter (GTK_TEXT_VIEW (view), &match_start,
- 0.0, FALSE, 0.0, 0.0);
- }
-}
-
-static void
-example_app_window_init (ExampleAppWindow *win)
-{
-
-...
-
- gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (class), search_text_changed);
-
-...
-
-}
-
-...
- ]]></programlisting>
- <para>(<ulink url="https://gitlab.gnome.org/GNOME/gtk/blob/master/examples/application7/exampleappwin.c">full source</ulink>)</para>
- </informalexample>
-
- <para>With the search bar, our application now looks like this:</para>
-
- <informalfigure>
- <mediaobject>
- <imageobject>
- <imagedata fileref="getting-started-app7.png" format="PNG"/>
- </imageobject>
- </mediaobject>
- </informalfigure>
- </section>
-
- <section>
- <title>Adding a side bar</title>
-
- <para>As another piece of functionality, we are adding a sidebar,
- which demonstrates #GtkMenuButton, #GtkRevealer and #GtkListBox.</para>
-
- <informalexample>
- <programlisting><xi:include href="../../../../examples/application8/window.ui" parse="text"><xi:fallback>MISSING XINCLUDE CONTENT</xi:fallback></xi:include></programlisting>
- </informalexample>
-
- <para>The code to populate the sidebar with buttons for the words
- found in each file is a little too involved to go into here. But we'll
- look at the code to add the gears menu.</para>
-
- <para>As expected by now, the gears menu is specified in a GtkBuilder
- ui file.</para>
-
- <informalexample>
- <programlisting><xi:include href="../../../../examples/application8/gears-menu.ui" parse="text"><xi:fallback>MISSING XINCLUDE CONTENT</xi:fallback></xi:include></programlisting>
- </informalexample>
-
- <para>To connect the menuitem to the show-words setting, we use
- a #GAction corresponding to the given #GSettings key.</para>
-
- <informalexample>
- <programlisting><![CDATA[
-...
-
-static void
-example_app_window_init (ExampleAppWindow *win)
-{
-
-...
-
- builder = gtk_builder_new_from_resource ("/org/gtk/exampleapp/gears-menu.ui");
- menu = G_MENU_MODEL (gtk_builder_get_object (builder, "menu"));
- gtk_menu_button_set_menu_model (GTK_MENU_BUTTON (priv->gears), menu);
- g_object_unref (builder);
-
- action = g_settings_create_action (priv->settings, "show-words");
- g_action_map_add_action (G_ACTION_MAP (win), action);
- g_object_unref (action);
-}
-
-...
- ]]></programlisting>
- <para>(<ulink url="https://gitlab.gnome.org/GNOME/gtk/blob/master/examples/application8/exampleappwin.c">full source</ulink>)</para>
- </informalexample>
-
- <para>What our application looks like now:</para>
-
- <informalfigure>
- <mediaobject>
- <imageobject>
- <imagedata fileref="getting-started-app8.png" format="PNG"/>
- </imageobject>
- </mediaobject>
- </informalfigure>
- </section>
- <section>
- <title>Properties</title>
-
- <para>Widgets and other objects have many useful properties.</para>
-
- <para>Here we show some ways to use them in new and flexible ways,
- by wrapping them in actions with #GPropertyAction or by binding them
- with #GBinding.</para>
-
- <para>To set this up, we add two labels to the header bar in our
- window template, named @lines_label and @lines, and bind them to
- struct members in the private struct, as we've seen a couple of times
- by now.</para>
-
- <para>We add a new "Lines" menu item to the gears menu, which
- triggers the show-lines action:</para>
-
- <informalexample>
- <programlisting><xi:include href="../../../../examples/application9/gears-menu.ui" parse="text"><xi:fallback>MISSING XINCLUDE CONTENT</xi:fallback></xi:include></programlisting>
- </informalexample>
-
- <para>To make this menu item do something, we create a property
- action for the visible property of the @lines label, and add it to the
- actions of the window. The effect of this is that the visibility
- of the label gets toggled every time the action is activated.</para>
-
- <para>Since we want both labels to appear and disappear together,
- we bind the visible property of the @lines_label widget to the
- same property of the @lines widget.</para>
-
- <informalexample>
- <programlisting>
-...
-
-static void
-example_app_window_init (ExampleAppWindow *win)
-{
- ...
-
- action = (GAction*) g_property_action_new ("show-lines", win->lines, "visible");
- g_action_map_add_action (G_ACTION_MAP (win), action);
- g_object_unref (action);
-
- g_object_bind_property (win->lines, "visible",
- win->lines_label, "visible",
- G_BINDING_DEFAULT);
-}
-
-...
- </programlisting>
- <para>(<ulink url="https://gitlab.gnome.org/GNOME/gtk/blob/master/examples/application9/exampleappwin.c">full source</ulink>)</para>
- </informalexample>
-
- <para>We also need a function that counts the lines of the currently
- active tab, and updates the @lines label. See the
- <ulink url="https://gitlab.gnome.org/GNOME/gtk/blob/master/examples/application9/exampleappwin.c">full source</ulink>
- if you are interested in the details.</para>
-
- <para>This brings our example application to this appearance:</para>
-
- <informalfigure>
- <mediaobject>
- <imageobject>
- <imagedata fileref="getting-started-app9.png" format="PNG"/>
- </imageobject>
- </mediaobject>
- </informalfigure>
- </section>
- <section>
- <title>Header bar</title>
-
- <para>Our application already uses a GtkHeaderBar, but so far it
- still gets a 'normal' window titlebar on top of that. This is a
- bit redundant, and we will now tell GTK+ to use the header bar
- as replacement for the titlebar. To do so, we move it around to
- be a direct child of the window, and set its type to be titlebar.</para>
-
- <informalexample>
- <programlisting><xi:include href="../../../../examples/application10/window.ui" parse="text"><xi:fallback>MISSING XINCLUDE CONTENT</xi:fallback></xi:include></programlisting>
- </informalexample>
-
- <para>A small extra bonus of using a header bar is that we get
- a fallback application menu for free. Here is how the
- application now looks, if this fallback is used.</para>
-
- <informalfigure>
- <mediaobject>
- <imageobject>
- <imagedata fileref="getting-started-app10.png" format="PNG"/>
- </imageobject>
- </mediaobject>
- </informalfigure>
-
- <para>If we set up the window icon for our window, the menu button
- will use that instead of the generic placeholder icon you see
- here.</para>
- </section>
- </section>
-
- <section>
- <title>Custom Drawing</title>
-
- <para>Many widgets, like buttons, do all their drawing themselves. You
- just tell them the label you want to see, and they figure out what font
- to use, draw the button outline and focus rectangle, etc. Sometimes, it
- is necessary to do some custom drawing. In that case, a #GtkDrawingArea
- might be the right widget to use. It offers a canvas on which you can
- draw by connecting to the #GtkWidget::draw signal.
- </para>
-
- <para>The contents of a widget often need to be partially or fully redrawn,
- e.g. when another window is moved and uncovers part of the widget, or
- when the window containing it is resized. It is also possible to explicitly
- cause part or all of the widget to be redrawn, by calling
- gtk_widget_queue_draw() or its variants. GTK+ takes care of most of the
- details by providing a ready-to-use cairo context to the ::draw signal
- handler.</para>
-
- <para>The following example shows a ::draw signal handler. It is a bit
- more complicated than the previous examples, since it also demonstrates
- input event handling by means of ::button-press and ::motion-notify
- handlers.</para>
-
- <informalfigure>
- <mediaobject>
- <imageobject>
- <imagedata fileref="drawing.png" format="PNG"/>
- </imageobject>
- </mediaobject>
- </informalfigure>
-
- <example id="gtk-getting-started-drawing">
- <title>Drawing in response to input</title>
- <para>Create a new file with the following content named example-4.c.</para>
- <programlisting><xi:include href="../../../../examples/drawing.c" parse="text"><xi:fallback>MISSING XINCLUDE CONTENT</xi:fallback></xi:include></programlisting>
- </example>
- <para>
- You can compile the program above with GCC using:
- <literallayout>
- <literal>gcc `pkg-config --cflags gtk+-4.0` -o example-4 example-4.c `pkg-config --libs gtk+-4.0`</literal>
- </literallayout>
- </para>
- </section>
-
-</chapter>
--- /dev/null
+<?xml version="1.0"?>
+<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
+ "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd" [
+]>
+<chapter id="gtk-getting-started" xmlns:xi="http://www.w3.org/2003/XInclude">
+ <title>Getting Started with GTK+</title>
+
+ <para>GTK+ is a <ulink url="http://en.wikipedia.org/wiki/Widget_toolkit">
+ widget toolkit</ulink>. Each user interface created by
+ GTK+ consists of widgets. This is implemented in C using
+ <link linkend="gobject">GObject</link>, an object-oriented framework for C.
+ Widgets are organized in a hierachy. The window widget is the main container.
+ The user interface is then built by adding buttons, drop-down menus, input
+ fields, and other widgets to the window.
+ If you are creating complex user interfaces it is recommended to
+ use #GtkBuilder and its GTK-specific markup description language, instead of
+ assembling the interface manually. You can also use a visual user interface
+ editor, like <ulink url="https://glade.gnome.org/">Glade</ulink>.</para>
+
+ <para>GTK+ is event-driven. The toolkit listens for events such as
+ a click on a button, and passes the event to your application.</para>
+
+ <para>This chapter contains some tutorial information to get you
+ started with GTK+ programming. It assumes that you have GTK+, its
+ dependencies and a C compiler installed and ready to use. If you
+ need to build GTK+ itself first, refer to the
+ <link linkend="gtk-compiling">Compiling the GTK+ libraries</link>
+ section in this reference.</para>
+
+ <section>
+ <title>Basics</title>
+
+ <para>To begin our introduction to GTK, we'll start with a simple
+ signal-based Gtk application. This program will create an empty 200 × 200 pixel
+ window.</para>
+
+ <informalfigure>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="window-default.png" format="PNG"/>
+ </imageobject>
+ </mediaobject>
+ </informalfigure>
+
+ <informalexample>
+ <para>Create a new file with the following content named <filename>example-0.c.</filename></para>
+ <programlisting><xi:include href="@SRC_DIR@/examples/window-default.c" parse="text"><xi:fallback>MISSING XINCLUDE CONTENT</xi:fallback></xi:include></programlisting>
+ </informalexample>
+
+ <para>
+ You can compile the program above with GCC using:
+ <literallayout>
+ <literal>gcc `pkg-config --cflags gtk+-4.0` -o example-0 example-0.c `pkg-config --libs gtk+-4.0`</literal>
+ </literallayout>
+ </para>
+
+ <note><para>For more information on how to compile a GTK+ application, please
+ refer to the <link linkend="gtk-compiling">Compiling GTK+ Applications</link>
+ section in this reference.</para></note>
+
+ <para>All GTK+ applications will, of course, include
+ <filename>gtk/gtk.h</filename>, which declares functions, types and
+ macros required by GTK+ applications.</para>
+
+ <warning><para>Even if GTK+ installs multiple header files, only the
+ top-level <filename>gtk/gtk.h</filename> header can be directly included
+ by third party code. The compiler will abort with an error if any other
+ header is directly included.</para></warning>
+
+ <para>In a GTK+ application, the purpose of the main() function is to
+ create a #GtkApplication object and run it. In this example a
+ #GtkApplication pointer named <varname>app</varname> is called and then
+ initialized using gtk_application_new().</para>
+
+ <para>When creating a #GtkApplication
+ you need to pick an application identifier (a name)
+ and input to gtk_application_new() as parameter.
+ For this example <varname>org.gtk.example</varname> is used
+ but for choosing an identifier for your application see
+ <ulink url="https://wiki.gnome.org/HowDoI/ChooseApplicationID">this guide</ulink>.
+ Lastly gtk_application_new() takes a GApplicationFlags as input for your
+ application, if your application would have special needs.
+ </para>
+
+ <para>Next the
+ <ulink url="https://wiki.gnome.org/HowDoI/GtkApplication">activate signal</ulink>
+ is connected to the activate() function above the main() functions.
+ The <varname>activate</varname> signal will be sent
+ when your application is launched with
+ g_application_run() on the line below.
+ The gtk_application_run() also takes as arguments the pointers to the command line arguments
+ counter and string array; this allows GTK+ to parse specific command line
+ arguments that control the behavior of GTK+ itself. The parsed arguments
+ will be removed from the array, leaving the unrecognized ones for your
+ application to parse.
+ </para>
+
+ <para>Within g_application_run the activate() signal is sent and
+ we then proceed into the <function>activate</function>() function of the
+ application. Inside the activate() function we want to construct
+ our GTK window, so that a window is shown when the application
+ is launched. The call to gtk_application_window_new() will
+ create a new #GtkWindow and store it inside the
+ <varname>window</varname> pointer. The window will have a frame,
+ a title bar, and window controls depending on the platform.</para>
+
+ <para>A window title is set using gtk_window_set_title(). This function
+ takes a GtkWindow* pointer and a string as input. As our
+ <varname>window</varname> pointer is a GtkWidget pointer, we need to cast it
+ to GtkWindow*.
+ But instead of casting <varname>window</varname> via
+ <varname>(GtkWindow*)</varname>,
+ <varname>window</varname> can be cast using the macro
+ <varname>GTK_WINDOW()</varname>.
+ <varname>GTK_WINDOW()</varname> will check if the
+ pointer is an instance of the GtkWindow class, before casting, and emit a
+ warning if the check fails. More information about this convention
+ can be found
+ <ulink url="https://developer.gnome.org/gobject/stable/gtype-conventions.html">
+ here</ulink>.</para>
+
+ <para>Finally the window size is set using gtk_window_set_default_size and
+ the window is then shown by GTK via gtk_widget_show().</para>
+
+ <para>When you exit the window, by for example pressing the X,
+ the g_application_run() in the main loop returns with a number
+ which is saved inside an integer named "status". Afterwards, the
+ #GtkApplication object is freed from memory with g_object_unref().
+ Finally the status integer is returned and the GTK application exits.</para>
+
+ <para>While the program is running, GTK+ is receiving
+ <firstterm>events</firstterm>. These are typically input events caused by
+ the user interacting with your program, but also things like messages from
+ the window manager or other applications. GTK+ processes these and as a
+ result, <firstterm>signals</firstterm> may be emitted on your widgets.
+ Connecting handlers for these signals is how you normally make your
+ program do something in response to user input.</para>
+
+ <para>The following example is slightly more complex, and tries to
+ showcase some of the capabilities of GTK+.</para>
+
+ <para>In the long tradition of programming languages and libraries,
+ it is called <emphasis>Hello, World</emphasis>.</para>
+
+ <informalfigure>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="hello-world.png" format="PNG"/>
+ </imageobject>
+ </mediaobject>
+ </informalfigure>
+
+ <example id="gtk-getting-started-hello-world">
+ <title>Hello World in GTK+</title>
+ <para>Create a new file with the following content named example-1.c.</para>
+ <programlisting><xi:include href="@SRC_DIR@/examples/hello-world.c" parse="text">
+ <xi:fallback>MISSING XINCLUDE CONTENT</xi:fallback></xi:include></programlisting>
+ </example>
+
+ <para>
+ You can compile the program above with GCC using:
+ <literallayout>
+ <literal>gcc `pkg-config --cflags gtk+-4.0` -o example-1 example-1.c `pkg-config --libs gtk+-4.0`</literal>
+ </literallayout>
+ </para>
+ </section>
+
+ <para>As seen above, example-1.c builds further upon example-0.c by adding a
+ button to our window, with the label "Hello World". Two new GtkWidget pointers
+ are declared to accomplish this, <varname>button</varname> and
+ <varname>button_box</varname>. The button_box variable is created to store a
+ #GtkButtonBox which is GTK+'s way of controlling the size and layout of buttons.
+ The #GtkButtonBox is created and assigned to gtk_button_box_new() which takes a
+ #GtkOrientation enum as parameter. The buttons which this box will contain can
+ either be stored horizontally or vertically but this does not matter in this
+ particular case as we are dealing with only one button. After initializing
+ button_box with horizontal orientation, the code adds the button_box widget to the
+ window widget using gtk_container_add().</para>
+
+ <para>Next the <varname>button</varname> variable is initialized in similar manner.
+ gtk_button_new_with_label() is called which returns a GtkButton to be stored inside
+ <varname>button</varname>. Afterwards <varname>button</varname> is added to
+ our <varname>button_box</varname>.
+ Using g_signal_connect the button is connected to a function in our app called
+ print_hello(), so that when the button is clicked, GTK will call this function.
+ As the print_hello() function does not use any data as input, NULL is passed
+ to it. print_hello() calls g_print() with the string "Hello World"
+ which will print Hello World in a terminal if the GTK application was started
+ from one.</para>
+
+ <para>After connecting print_hello(), another signal is connected to the "clicked" state
+ of the button using g_signal_connect_swapped(). This functions is similar to
+ a g_signal_connect() with the difference lying in how the callback function is
+ treated. g_signal_connect_swapped() allow you to specify what the callback
+ function should take as parameter by letting you pass it as data. In this case
+ the function being called back is gtk_widget_destroy() and the <varname>window</varname>
+ pointer is passed to it. This has the effect that when the button is clicked,
+ the whole GTK window is destroyed. In contrast if a normal g_signal_connect() were used
+ to connect the "clicked" signal with gtk_widget_destroy(), then the <varname>button</varname>
+ itself would have been destroyed, not the window.
+ More information about creating buttons can be found
+ <ulink url="https://wiki.gnome.org/HowDoI/Buttons">here</ulink>.
+ </para>
+
+ <para>The rest of the code in example-1.c is identical to example-0.c. Next
+ section will elaborate further on how to add several GtkWidgets to your GTK
+ application.</para>
+
+ <section>
+ <title>Packing</title>
+
+ <para>When creating an application, you'll want to put more than one widget
+ inside a window.
+ When you want to put more than one widget into a window,
+ it becomes important to control how each widget is positioned and sized.
+ This is where packing comes in.</para>
+
+ <para>GTK+ comes with a large variety of <firstterm>layout containers</firstterm>
+ whose purpose it is to control the layout of the child widgets that are
+ added to them. See <xref linkend="LayoutContainers"/> for an overview.</para>
+
+ <para>The following example shows how the GtkGrid container lets you
+ arrange several buttons:</para>
+
+ <informalfigure>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="grid-packing.png" format="PNG"/>
+ </imageobject>
+ </mediaobject>
+ </informalfigure>
+
+ <example id="gtk-getting-started-grid-packing">
+ <title>Packing buttons</title>
+ <para>Create a new file with the following content named example-2.c.</para>
+ <programlisting><xi:include href="@SRC_DIR@/examples/grid-packing.c" parse="text"><xi:fallback>MISSING XINCLUDE CONTENT</xi:fallback></xi:include></programlisting>
+ </example>
+ <para>
+ You can compile the program above with GCC using:
+ <literallayout>
+ <literal>gcc `pkg-config --cflags gtk+-4.0` -o example-2 example-2.c `pkg-config --libs gtk+-4.0`</literal>
+ </literallayout>
+ </para>
+ </section>
+
+ <section>
+ <title>Building user interfaces</title>
+
+ <para>When construcing a more complicated user interface, with dozens
+ or hundreds of widgets, doing all the setup work in C code is
+ cumbersome, and making changes becomes next to impossible.</para>
+
+ <para>Thankfully, GTK+ supports the separation of user interface
+ layout from your business logic, by using UI descriptions in an
+ XML format that can be parsed by the #GtkBuilder class.</para>
+
+ <example>
+ <title>Packing buttons with GtkBuilder</title>
+ <para>Create a new file with the following content named example-3.c.</para>
+ <programlisting><xi:include href="@SRC_DIR@/examples/builder.c" parse="text"><xi:fallback>MISSING XINCLUDE CONTENT</xi:fallback></xi:include></programlisting>
+ <para>Create a new file with the following content named builder.ui.</para>
+ <programlisting><xi:include href="@SRC_DIR@/examples/builder.ui" parse="text"><xi:fallback>MISSING XINCLUDE CONTENT</xi:fallback></xi:include></programlisting>
+ </example>
+ <para>
+ You can compile the program above with GCC using:
+ <literallayout>
+ <literal>gcc `pkg-config --cflags gtk+-4.0` -o example-3 example-3.c `pkg-config --libs gtk+-4.0`</literal>
+ </literallayout>
+ </para>
+
+ <para>Note that GtkBuilder can also be used to construct objects
+ that are not widgets, such as tree models, adjustments, etc.
+ That is the reason the method we use here is called
+ gtk_builder_get_object() and returns a GObject* instead of a
+ GtkWidget*.</para>
+
+ <para>Normally, you would pass a full path to
+ gtk_builder_add_from_file() to make the execution of your program
+ independent of the current directory. A common location to install
+ UI descriptions and similar data is
+ <filename>/usr/share/<replaceable>appname</replaceable></filename>.
+ </para>
+
+ <para>It is also possible to embed the UI description in the source
+ code as a string and use gtk_builder_add_from_string() to load it.
+ But keeping the UI description in a separate file has several
+ advantages: It is then possible to make minor adjustments to the UI
+ without recompiling your program, and, more importantly, graphical
+ UI editors such as <ulink url="http://glade.gnome.org">glade</ulink>
+ can load the file and allow you to create and modify your UI by
+ point-and-click.</para>
+ </section>
+
+ <section>
+ <title>Building applications</title>
+
+ <para>An application consists of a number of files:
+ <variablelist>
+ <varlistentry>
+ <term>The binary</term>
+ <listitem>This gets installed in <filename>/usr/bin</filename>.</listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>A desktop file</term>
+ <listitem>The desktop file provides important information about the application to the desktop shell, such as its name, icon, D-Bus name, commandline to launch it, etc. It is installed in <filename>/usr/share/applications</filename>.</listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>An icon</term>
+ <listitem>The icon gets installed in <filename>/usr/share/icons/hicolor/48x48/apps</filename>, where it will be found regardless of the current theme.</listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>A settings schema</term>
+ <listitem>If the application uses GSettings, it will install its schema
+ in <filename>/usr/share/glib-2.0/schemas</filename>, so that tools
+ like dconf-editor can find it.</listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Other resources</term>
+ <listitem>Other files, such as GtkBuilder ui files, are best loaded from
+ resources stored in the application binary itself. This eliminates the
+ need for most of the files that would traditionally be installed in
+ an application-specific location in <filename>/usr/share</filename>.</listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+
+ <para>GTK+ includes application support that is built on top of
+ #GApplication. In this tutorial we'll build a simple application by
+ starting from scratch, adding more and more pieces over time. Along
+ the way, we'll learn about #GtkApplication, templates, resources,
+ application menus, settings, #GtkHeaderBar, #GtkStack, #GtkSearchBar,
+ #GtkListBox, and more.</para>
+
+ <para>The full, buildable sources for these examples can be found
+ in the examples/ directory of the GTK+ source distribution, or
+ <ulink url="https://gitlab.gnome.org/GNOME/gtk/blob/master/examples">online</ulink> in the GTK+ git repository.
+ You can build each example separately by using make with the <filename>Makefile.example</filename>
+ file. For more information, see the <filename>README</filename> included in the
+ examples directory.</para>
+
+ <section>
+ <title>A trivial application</title>
+
+ <para>When using #GtkApplication, the main() function can be very
+ simple. We just call g_application_run() and give it an instance
+ of our application class.</para>
+
+ <informalexample>
+ <programlisting><xi:include href="@SRC_DIR@/examples/application1/main.c" parse="text"><xi:fallback>MISSING XINCLUDE CONTENT</xi:fallback></xi:include></programlisting>
+ </informalexample>
+
+ <para>All the application logic is in the application class, which
+ is a subclass of #GtkApplication. Our example does not yet have any
+ interesting functionality. All it does is open a window when it is
+ activated without arguments, and open the files it is given, if it
+ is started with arguments.</para>
+
+ <para>To handle these two cases, we override the activate() vfunc,
+ which gets called when the application is launched without commandline
+ arguments, and the open() vfunc, which gets called when the application
+ is launched with commandline arguments.</para>
+
+ <para>To learn more about GApplication entry points, consult the
+ GIO <ulink url="https://developer.gnome.org/gio/2.36/GApplication.html#GApplication.description">documentation</ulink>.</para>
+
+ <informalexample>
+ <programlisting><xi:include href="@SRC_DIR@/examples/application1/exampleapp.c" parse="text"><xi:fallback>MISSING XINCLUDE CONTENT</xi:fallback></xi:include></programlisting>
+ </informalexample>
+
+ <para>Another important class that is part of the application support
+ in GTK+ is #GtkApplicationWindow. It is typically subclassed as well.
+ Our subclass does not do anything yet, so we will just get an empty
+ window.</para>
+
+ <informalexample>
+ <programlisting><xi:include href="@SRC_DIR@/examples/application1/exampleappwin.c" parse="text"><xi:fallback>MISSING XINCLUDE CONTENT</xi:fallback></xi:include></programlisting>
+ </informalexample>
+
+ <para>As part of the initial setup of our application, we also
+ create an icon and a desktop file.</para>
+
+ <informalfigure>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="exampleapp.png" format="PNG"/>
+ </imageobject>
+ </mediaobject>
+ </informalfigure>
+
+ <informalexample>
+ <programlisting><xi:include href="@SRC_DIR@/examples/application1/org.gtk.exampleapp.desktop" parse="text"><xi:fallback>MISSING XINCLUDE CONTENT</xi:fallback></xi:include></programlisting>
+ </informalexample>
+
+ <para>Note that <replaceable>@<!-- -->bindir@</replaceable> needs to be replaced
+ with the actual path to the binary before this desktop file can be used.</para>
+
+ <para>Here is what we've achieved so far:</para>
+
+ <informalfigure>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="getting-started-app1.png" format="PNG"/>
+ </imageobject>
+ </mediaobject>
+ </informalfigure>
+
+ <para>This does not look very impressive yet, but our application
+ is already presenting itself on the session bus, it has single-instance
+ semantics, and it accepts files as commandline arguments.</para>
+ </section>
+
+ <section>
+ <title>Populating the window</title>
+
+ <para>In this step, we use a #GtkBuilder template to associate a
+ #GtkBuilder ui file with our application window class.</para>
+ <para>Our simple ui file puts a #GtkHeaderBar on top of a #GtkStack
+ widget. The header bar contains a #GtkStackSwitcher, which is a
+ standalone widget to show a row of 'tabs' for the pages of a #GtkStack.
+ </para>
+
+ <informalexample>
+ <programlisting><xi:include href="@SRC_DIR@/examples/application2/window.ui" parse="text"><xi:fallback>MISSING XINCLUDE CONTENT</xi:fallback></xi:include></programlisting>
+ </informalexample>
+
+ <para>To make use of this file in our application, we revisit
+ our #GtkApplicationWindow subclass, and call
+ gtk_widget_class_set_template_from_resource() from the class init
+ function to set the ui file as template for this class. We also
+ add a call to gtk_widget_init_template() in the instance init
+ function to instantiate the template for each instance of our
+ class.</para>
+
+ <informalexample>
+ <programlisting><![CDATA[
+ ...
+
+static void
+example_app_window_init (ExampleAppWindow *win)
+{
+ gtk_widget_init_template (GTK_WIDGET (win));
+}
+
+static void
+example_app_window_class_init (ExampleAppWindowClass *class)
+{
+ gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (class),
+ "/org/gtk/exampleapp/window.ui");
+}
+
+ ...
+ ]]></programlisting>
+ <para>(<ulink url="https://gitlab.gnome.org/GNOME/gtk/blob/master/examples/application2/exampleappwin.c">full source</ulink>)</para>
+ </informalexample>
+
+ <para>You may have noticed that we used the <literal>_from_resource(<!-- -->)</literal> variant
+ of the function that sets a template. Now we need to use <ulink url="https://developer.gnome.org/gio/stable/GResource.html">GLib's resource functionality</ulink>
+ to include the ui file in the binary. This is commonly done by listing
+ all resources in a .gresource.xml file, such as this:
+ </para>
+
+ <informalexample>
+ <programlisting><xi:include href="@SRC_DIR@/examples/application2/exampleapp.gresource.xml" parse="text"><xi:fallback>MISSING XINCLUDE CONTENT</xi:fallback></xi:include></programlisting>
+ </informalexample>
+
+ <para>This file has to be converted into a C source file that will be
+ compiled and linked into the application together with the other source
+ files. To do so, we use the glib-compile-resources utility:</para>
+
+ <screen>
+ glib-compile-resources exampleapp.gresource.xml --target=resources.c --generate-source
+ </screen>
+
+ <para>Our application now looks like this:</para>
+
+ <informalfigure>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="getting-started-app2.png" format="PNG"/>
+ </imageobject>
+ </mediaobject>
+ </informalfigure>
+ </section>
+
+ <section>
+ <title>Opening files</title>
+
+ <para>In this step, we make our application show the content of
+ all the files that it is given on the commandline.</para>
+
+ <para>To this end, we add a member to the struct in application
+ window subclass and keep a reference to the #GtkStack there.
+ The first member of the struct should be the parent type from
+ which the class is derived. Here, ExampleAppWindow is derived
+ from GtkApplicationWindow.
+ The gtk_widget_class_bind_template_child() function
+ arranges things so that after instantiating the template, the
+ @stack member of the struct will point to the widget of
+ the same name from the template.</para>
+
+ <informalexample>
+ <programlisting><![CDATA[
+...
+
+struct _ExampleAppWindow
+{
+ GtkApplicationWindow parent;
+
+ GtkWidget *stack;
+};
+
+G_DEFINE_TYPE (ExampleAppWindow, example_app_window, GTK_TYPE_APPLICATION_WINDOW)
+
+...
+
+static void
+example_app_window_class_init (ExampleAppWindowClass *class)
+{
+ gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (class),
+ "/org/gtk/exampleapp/window.ui");
+ gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), ExampleAppWindow, stack);
+}
+
+...
+ ]]></programlisting>
+ <para>(<ulink url="https://gitlab.gnome.org/GNOME/gtk/blob/master/examples/application3/exampleappwin.c">full source</ulink>)</para>
+ </informalexample>
+
+ <para>Now we revisit the example_app_window_open() function that
+ is called for each commandline argument, and construct a GtkTextView
+ that we then add as a page to the stack:</para>
+
+ <informalexample>
+ <programlisting><![CDATA[
+...
+
+void
+example_app_window_open (ExampleAppWindow *win,
+ GFile *file)
+{
+ gchar *basename;
+ GtkWidget *scrolled, *view;
+ gchar *contents;
+ gsize length;
+
+ basename = g_file_get_basename (file);
+
+ scrolled = gtk_scrolled_window_new (NULL, NULL);
+ gtk_widget_set_hexpand (scrolled, TRUE);
+ gtk_widget_set_vexpand (scrolled, TRUE);
+ view = gtk_text_view_new ();
+ gtk_text_view_set_editable (GTK_TEXT_VIEW (view), FALSE);
+ gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (view), FALSE);
+ gtk_container_add (GTK_CONTAINER (scrolled), view);
+ gtk_stack_add_titled (GTK_STACK (win->stack), scrolled, basename, basename);
+
+ if (g_file_load_contents (file, NULL, &contents, &length, NULL, NULL))
+ {
+ GtkTextBuffer *buffer;
+
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
+ gtk_text_buffer_set_text (buffer, contents, length);
+ g_free (contents);
+ }
+
+ g_free (basename);
+}
+
+...
+ ]]></programlisting>
+ <para>(<ulink url="https://gitlab.gnome.org/GNOME/gtk/blob/master/examples/application3/exampleappwin.c">full source</ulink>)</para>
+ </informalexample>
+
+ <para>Note that we did not have to touch the stack switcher
+ at all. It gets all its information from the stack that it
+ belongs to. Here, we are passing the label to show for each
+ file as the last argument to the gtk_stack_add_titled()
+ function.</para>
+
+ <para>Our application is beginning to take shape:</para>
+
+ <informalfigure>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="getting-started-app3.png" format="PNG"/>
+ </imageobject>
+ </mediaobject>
+ </informalfigure>
+ </section>
+
+ <section>
+ <title>An application menu</title>
+
+ <para>An application menu is shown by GNOME shell at the top of the
+ screen. It is meant to collect infrequently used actions that affect
+ the whole application.</para>
+
+ <para>Just like the window template, we specify our application menu
+ in a ui file, and add it as a resource to our binary.</para>
+
+ <informalexample>
+ <programlisting><xi:include href="@SRC_DIR@/examples/application4/app-menu.ui" parse="text"><xi:fallback>MISSING XINCLUDE CONTENT</xi:fallback></xi:include></programlisting>
+ </informalexample>
+
+ <para>To associate the app menu with the application, we have to call
+ gtk_application_set_app_menu(). Since app menus work by activating
+ #GActions, we also have to add a suitable set of actions to our
+ application.</para>
+
+ <para>Both of these tasks are best done in the startup() vfunc,
+ which is guaranteed to be called once for each primary application
+ instance:</para>
+ <informalexample>
+ <programlisting>
+...
+
+static void
+preferences_activated (GSimpleAction *action,
+ GVariant *parameter,
+ gpointer app)
+{
+}
+
+static void
+quit_activated (GSimpleAction *action,
+ GVariant *parameter,
+ gpointer app)
+{
+ g_application_quit (G_APPLICATION (app));
+}
+
+static GActionEntry app_entries[] =
+{
+ { "preferences", preferences_activated, NULL, NULL, NULL },
+ { "quit", quit_activated, NULL, NULL, NULL }
+};
+
+static void
+example_app_startup (GApplication *app)
+{
+ GtkBuilder *builder;
+ GMenuModel *app_menu;
+ const gchar *quit_accels[2] = { "<Ctrl>Q", NULL };
+
+ G_APPLICATION_CLASS (example_app_parent_class)->startup (app);
+
+ g_action_map_add_action_entries (G_ACTION_MAP (app),
+ app_entries, G_N_ELEMENTS (app_entries),
+ app);
+ gtk_application_set_accels_for_action (GTK_APPLICATION (app),
+ "app.quit",
+ quit_accels);
+
+ builder = gtk_builder_new_from_resource ("/org/gtk/exampleapp/app-menu.ui");
+ app_menu = G_MENU_MODEL (gtk_builder_get_object (builder, "appmenu"));
+ gtk_application_set_app_menu (GTK_APPLICATION (app), app_menu);
+ g_object_unref (builder);
+}
+
+static void
+example_app_class_init (ExampleAppClass *class)
+{
+ G_APPLICATION_CLASS (class)->startup = example_app_startup;
+ ...
+}
+
+...
+ </programlisting>
+ <para>(<ulink url="https://gitlab.gnome.org/GNOME/gtk/blob/master/examples/application4/exampleapp.c">full source</ulink>)</para>
+ </informalexample>
+
+ <para>Our preferences menu item does not do anything yet,
+ but the Quit menu item is fully functional. Note that it
+ can also be activated by the usual Ctrl-Q shortcut. The
+ shortcut was added with gtk_application_set_accels_for_action().
+ </para>
+
+ <para>The application menu looks like this:</para>
+
+ <informalfigure>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="getting-started-app4.png" format="PNG"/>
+ </imageobject>
+ </mediaobject>
+ </informalfigure>
+ </section>
+
+ <section>
+ <title>A preference dialog</title>
+
+ <para>A typical application will have a some preferences that
+ should be remembered from one run to the next. Even for our
+ simple example application, we may want to change the font
+ that is used for the content.</para>
+
+ <para>We are going to use GSettings to store our preferences.
+ GSettings requires a schema that describes our settings:</para>
+
+ <informalexample>
+ <programlisting><xi:include href="@SRC_DIR@/examples/application5/org.gtk.exampleapp.gschema.xml" parse="text"><xi:fallback>MISSING XINCLUDE CONTENT</xi:fallback></xi:include></programlisting>
+ </informalexample>
+
+ <para>Before we can make use of this schema in our application,
+ we need to compile it into the binary form that GSettings
+ expects. GIO provides <ulink url="https://developer.gnome.org/gio/2.36/ch31s06.html">macros</ulink>
+ to do this in autotools-based projects.</para>
+
+ <para>Next, we need to connect our settings to the widgets
+ that they are supposed to control. One convenient way to do
+ this is to use GSettings bind functionality to bind settings
+ keys to object properties, as we do here for the transition
+ setting.</para>
+
+ <informalexample>
+ <programlisting><![CDATA[
+...
+
+static void
+example_app_window_init (ExampleAppWindow *win)
+{
+ gtk_widget_init_template (GTK_WIDGET (win));
+ win->settings = g_settings_new ("org.gtk.exampleapp");
+
+ g_settings_bind (win->settings, "transition",
+ win->stack, "transition-type",
+ G_SETTINGS_BIND_DEFAULT);
+}
+
+...
+ ]]></programlisting>
+ <para>(<ulink url="https://gitlab.gnome.org/GNOME/gtk/blob/master/examples/application5/exampleappwin.c">full source</ulink>)</para>
+ </informalexample>
+
+ <para>The code to connect the font setting is a little more involved,
+ since there is no simple object property that it corresponds to, so
+ we are not going to go into that here.</para>
+
+ <para>At this point, the application will already react if you
+ change one of the settings, e.g. using the gsettings commandline
+ tool. Of course, we expect the application to provide a preference
+ dialog for these. So lets do that now. Our preference dialog will
+ be a subclass of GtkDialog, and we'll use the same techniques that
+ we've already seen: templates, private structs, settings
+ bindings.</para>
+
+ <para>Lets start with the template.</para>
+
+ <informalexample>
+ <programlisting><xi:include href="@SRC_DIR@/examples/application6/prefs.ui" parse="text"><xi:fallback>MISSING XINCLUDE CONTENT</xi:fallback></xi:include></programlisting>
+ </informalexample>
+
+ <para>Next comes the dialog subclass.</para>
+
+ <informalexample>
+ <programlisting><xi:include href="@SRC_DIR@/examples/application6/exampleappprefs.c" parse="text"><xi:fallback>MISSING XINCLUDE CONTENT</xi:fallback></xi:include></programlisting>
+ </informalexample>
+
+ <para>Now we revisit the <literal>preferences_activated(<!-- -->)</literal> function in our
+ application class, and make it open a new preference dialog.</para>
+
+ <informalexample>
+ <programlisting><![CDATA[
+...
+
+static void
+preferences_activated (GSimpleAction *action,
+ GVariant *parameter,
+ gpointer app)
+{
+ ExampleAppPrefs *prefs;
+ GtkWindow *win;
+
+ win = gtk_application_get_active_window (GTK_APPLICATION (app));
+ prefs = example_app_prefs_new (EXAMPLE_APP_WINDOW (win));
+ gtk_window_present (GTK_WINDOW (prefs));
+}
+
+...
+ ]]></programlisting>
+ <para>(<ulink url="https://gitlab.gnome.org/GNOME/gtk/blob/master/examples/application6/exampleapp.c">full source</ulink>)</para>
+ </informalexample>
+
+ <para>After all this work, our application can now show
+ a preference dialog like this:</para>
+
+ <informalfigure>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="getting-started-app6.png" format="PNG"/>
+ </imageobject>
+ </mediaobject>
+ </informalfigure>
+ </section>
+
+ <section>
+ <title>Adding a search bar</title>
+
+ <para>We continue to flesh out the functionality of our application.
+ For now, we add search. GTK+ supports this with #GtkSearchEntry and
+ #GtkSearchBar. The search bar is a widget that can slide in from the
+ top to present a search entry.</para>
+
+ <para>We add a toggle button to the header bar, which can be used
+ to slide out the search bar below the header bar.</para>
+
+ <informalexample>
+ <programlisting><xi:include href="@SRC_DIR@/examples/application7/window.ui" parse="text"><xi:fallback>MISSING XINCLUDE CONTENT</xi:fallback></xi:include></programlisting>
+ </informalexample>
+
+ <para>Implementing the search needs quite a few code changes that
+ we are not going to completely go over here. The central piece of
+ the search implementation is a signal handler that listens for
+ text changes in the search entry.</para>
+
+ <informalexample>
+ <programlisting><![CDATA[
+...
+
+static void
+search_text_changed (GtkEntry *entry,
+ ExampleAppWindow *win)
+{
+ const gchar *text;
+ GtkWidget *tab;
+ GtkWidget *view;
+ GtkTextBuffer *buffer;
+ GtkTextIter start, match_start, match_end;
+
+ text = gtk_entry_get_text (entry);
+
+ if (text[0] == '\0')
+ return;
+
+ tab = gtk_stack_get_visible_child (GTK_STACK (win->stack));
+ view = gtk_bin_get_child (GTK_BIN (tab));
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
+
+ /* Very simple-minded search implementation */
+ gtk_text_buffer_get_start_iter (buffer, &start);
+ if (gtk_text_iter_forward_search (&start, text, GTK_TEXT_SEARCH_CASE_INSENSITIVE,
+ &match_start, &match_end, NULL))
+ {
+ gtk_text_buffer_select_range (buffer, &match_start, &match_end);
+ gtk_text_view_scroll_to_iter (GTK_TEXT_VIEW (view), &match_start,
+ 0.0, FALSE, 0.0, 0.0);
+ }
+}
+
+static void
+example_app_window_init (ExampleAppWindow *win)
+{
+
+...
+
+ gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (class), search_text_changed);
+
+...
+
+}
+
+...
+ ]]></programlisting>
+ <para>(<ulink url="https://gitlab.gnome.org/GNOME/gtk/blob/master/examples/application7/exampleappwin.c">full source</ulink>)</para>
+ </informalexample>
+
+ <para>With the search bar, our application now looks like this:</para>
+
+ <informalfigure>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="getting-started-app7.png" format="PNG"/>
+ </imageobject>
+ </mediaobject>
+ </informalfigure>
+ </section>
+
+ <section>
+ <title>Adding a side bar</title>
+
+ <para>As another piece of functionality, we are adding a sidebar,
+ which demonstrates #GtkMenuButton, #GtkRevealer and #GtkListBox.</para>
+
+ <informalexample>
+ <programlisting><xi:include href="@SRC_DIR@/examples/application8/window.ui" parse="text"><xi:fallback>MISSING XINCLUDE CONTENT</xi:fallback></xi:include></programlisting>
+ </informalexample>
+
+ <para>The code to populate the sidebar with buttons for the words
+ found in each file is a little too involved to go into here. But we'll
+ look at the code to add the gears menu.</para>
+
+ <para>As expected by now, the gears menu is specified in a GtkBuilder
+ ui file.</para>
+
+ <informalexample>
+ <programlisting><xi:include href="@SRC_DIR@/examples/application8/gears-menu.ui" parse="text"><xi:fallback>MISSING XINCLUDE CONTENT</xi:fallback></xi:include></programlisting>
+ </informalexample>
+
+ <para>To connect the menuitem to the show-words setting, we use
+ a #GAction corresponding to the given #GSettings key.</para>
+
+ <informalexample>
+ <programlisting><![CDATA[
+...
+
+static void
+example_app_window_init (ExampleAppWindow *win)
+{
+
+...
+
+ builder = gtk_builder_new_from_resource ("/org/gtk/exampleapp/gears-menu.ui");
+ menu = G_MENU_MODEL (gtk_builder_get_object (builder, "menu"));
+ gtk_menu_button_set_menu_model (GTK_MENU_BUTTON (priv->gears), menu);
+ g_object_unref (builder);
+
+ action = g_settings_create_action (priv->settings, "show-words");
+ g_action_map_add_action (G_ACTION_MAP (win), action);
+ g_object_unref (action);
+}
+
+...
+ ]]></programlisting>
+ <para>(<ulink url="https://gitlab.gnome.org/GNOME/gtk/blob/master/examples/application8/exampleappwin.c">full source</ulink>)</para>
+ </informalexample>
+
+ <para>What our application looks like now:</para>
+
+ <informalfigure>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="getting-started-app8.png" format="PNG"/>
+ </imageobject>
+ </mediaobject>
+ </informalfigure>
+ </section>
+ <section>
+ <title>Properties</title>
+
+ <para>Widgets and other objects have many useful properties.</para>
+
+ <para>Here we show some ways to use them in new and flexible ways,
+ by wrapping them in actions with #GPropertyAction or by binding them
+ with #GBinding.</para>
+
+ <para>To set this up, we add two labels to the header bar in our
+ window template, named @lines_label and @lines, and bind them to
+ struct members in the private struct, as we've seen a couple of times
+ by now.</para>
+
+ <para>We add a new "Lines" menu item to the gears menu, which
+ triggers the show-lines action:</para>
+
+ <informalexample>
+ <programlisting><xi:include href="@SRC_DIR@/examples/application9/gears-menu.ui" parse="text"><xi:fallback>MISSING XINCLUDE CONTENT</xi:fallback></xi:include></programlisting>
+ </informalexample>
+
+ <para>To make this menu item do something, we create a property
+ action for the visible property of the @lines label, and add it to the
+ actions of the window. The effect of this is that the visibility
+ of the label gets toggled every time the action is activated.</para>
+
+ <para>Since we want both labels to appear and disappear together,
+ we bind the visible property of the @lines_label widget to the
+ same property of the @lines widget.</para>
+
+ <informalexample>
+ <programlisting>
+...
+
+static void
+example_app_window_init (ExampleAppWindow *win)
+{
+ ...
+
+ action = (GAction*) g_property_action_new ("show-lines", win->lines, "visible");
+ g_action_map_add_action (G_ACTION_MAP (win), action);
+ g_object_unref (action);
+
+ g_object_bind_property (win->lines, "visible",
+ win->lines_label, "visible",
+ G_BINDING_DEFAULT);
+}
+
+...
+ </programlisting>
+ <para>(<ulink url="https://gitlab.gnome.org/GNOME/gtk/blob/master/examples/application9/exampleappwin.c">full source</ulink>)</para>
+ </informalexample>
+
+ <para>We also need a function that counts the lines of the currently
+ active tab, and updates the @lines label. See the
+ <ulink url="https://gitlab.gnome.org/GNOME/gtk/blob/master/examples/application9/exampleappwin.c">full source</ulink>
+ if you are interested in the details.</para>
+
+ <para>This brings our example application to this appearance:</para>
+
+ <informalfigure>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="getting-started-app9.png" format="PNG"/>
+ </imageobject>
+ </mediaobject>
+ </informalfigure>
+ </section>
+ <section>
+ <title>Header bar</title>
+
+ <para>Our application already uses a GtkHeaderBar, but so far it
+ still gets a 'normal' window titlebar on top of that. This is a
+ bit redundant, and we will now tell GTK+ to use the header bar
+ as replacement for the titlebar. To do so, we move it around to
+ be a direct child of the window, and set its type to be titlebar.</para>
+
+ <informalexample>
+ <programlisting><xi:include href="@SRC_DIR@/examples/application10/window.ui" parse="text"><xi:fallback>MISSING XINCLUDE CONTENT</xi:fallback></xi:include></programlisting>
+ </informalexample>
+
+ <para>A small extra bonus of using a header bar is that we get
+ a fallback application menu for free. Here is how the
+ application now looks, if this fallback is used.</para>
+
+ <informalfigure>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="getting-started-app10.png" format="PNG"/>
+ </imageobject>
+ </mediaobject>
+ </informalfigure>
+
+ <para>If we set up the window icon for our window, the menu button
+ will use that instead of the generic placeholder icon you see
+ here.</para>
+ </section>
+ </section>
+
+ <section>
+ <title>Custom Drawing</title>
+
+ <para>Many widgets, like buttons, do all their drawing themselves. You
+ just tell them the label you want to see, and they figure out what font
+ to use, draw the button outline and focus rectangle, etc. Sometimes, it
+ is necessary to do some custom drawing. In that case, a #GtkDrawingArea
+ might be the right widget to use. It offers a canvas on which you can
+ draw by connecting to the #GtkWidget::draw signal.
+ </para>
+
+ <para>The contents of a widget often need to be partially or fully redrawn,
+ e.g. when another window is moved and uncovers part of the widget, or
+ when the window containing it is resized. It is also possible to explicitly
+ cause part or all of the widget to be redrawn, by calling
+ gtk_widget_queue_draw() or its variants. GTK+ takes care of most of the
+ details by providing a ready-to-use cairo context to the ::draw signal
+ handler.</para>
+
+ <para>The following example shows a ::draw signal handler. It is a bit
+ more complicated than the previous examples, since it also demonstrates
+ input event handling by means of ::button-press and ::motion-notify
+ handlers.</para>
+
+ <informalfigure>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="drawing.png" format="PNG"/>
+ </imageobject>
+ </mediaobject>
+ </informalfigure>
+
+ <example id="gtk-getting-started-drawing">
+ <title>Drawing in response to input</title>
+ <para>Create a new file with the following content named example-4.c.</para>
+ <programlisting><xi:include href="@SRC_DIR@/examples/drawing.c" parse="text"><xi:fallback>MISSING XINCLUDE CONTENT</xi:fallback></xi:include></programlisting>
+ </example>
+ <para>
+ You can compile the program above with GCC using:
+ <literallayout>
+ <literal>gcc `pkg-config --cflags gtk+-4.0` -o example-4 example-4.c `pkg-config --libs gtk+-4.0`</literal>
+ </literallayout>
+ </para>
+ </section>
+
+</chapter>
'css-overview.xml',
'css-properties.xml',
'drawing-model.xml',
- 'getting_started.xml',
'glossary.xml',
'gtk4-broadwayd.xml',
'gtk4-builder-tool.xml',
expand_content_files = [
'compiling.sgml',
'drawing-model.xml',
- 'getting_started.xml',
'glossary.xml',
'input-handling.xml',
'migrating-2to4.xml',
]
configure_file(input: 'version.xml.in', output: 'version.xml', configuration: version_conf)
+configure_file(input: 'getting_started.xml.in', output: 'getting_started.xml', configuration: src_dir_conf)
types_conf = configuration_data()
if os_win32
version_conf = configuration_data()
version_conf.set('GTK_VERSION', meson.project_version())
+src_dir_conf = configuration_data()
+src_dir_conf.set('SRC_DIR', meson.source_root())
+
subdir('gdk')
subdir('gsk')
subdir('gtk')